
local rulematch = ngx.re.find
local unescape = ngx.unescape_uri

local config = require("config")
local string = require("string")
local util = require("util")

local _M = {
    RULES = {}
}

function _M.load_rules()
    --_M.RULES = util.get_rules(config.rule_dir)
    --_M.RULES = util.get_rules_json()
    ngx.log(ngx.INFO,"load_rules")
    util.insert_wafrules_table()
    util.load_blackip()
    --for k, v in pairs(_M.RULES)
    for k, v in pairs(util.RULE_TABLE)
    do
        ngx.log(ngx.INFO, string.format("%s:load_rule", k))
        --for kk, vv in pairs(v)
        --do
        --    ngx.log(ngx.INFO, string.format("type:%s,index:%s,Rule:%s",k,kk, vv))
        --end
    end
    --return _M.RULES
end

function _M.get_rule(rule_file_name)
    --ngx.log(ngx.DEBUG, rule_file_name)
    --ngx.log(ngx.DEBUG, util.RULE_TABLE.useragent)
    --return _M.RULES[rule_file_name]
    return util.RULE_TABLE[rule_file_name]
end

-- exclude 
function _M.exclude_action()
    ngx.log(ngx.DEBUG,"xwaf_exclude_action")
   --ngx.log(ngx.DEBUG,ngx.req.get_method())
   --ngx.log(ngx.DEBUG,ngx.var.request_method)

    if ngx.var.request_method == "OPTIONS"  or ngx.var.request_method == "HEAD" then
        return true
    end

    if ngx.var.uri == "/xf/verify.html" then
        ngx.header.content_type = "text/html"
        ngx.status = ngx.HTTP_OK
        ngx.say(config.verify_html)
        ngx.exit(200)
    end

    if rulematch(ngx.var.uri,"/403.html|/404.html|/499.html|/500.html|/502.html|/503.html|/504.html|/50x.html|/xf/verify.html","joi") then
        return true
    end

    --if ngx.var.request_method == "POST" then
    --    local args = ngx.req.get_post_args()
    --    if args ~= nil then
    --        for key, val in pairs(args) do
    --            if key == "ngix_poster" then 
    --                --ngx.log(ngx.DEBUG,key)
    --                return true
    --            end
    --        end
    --    end
    --end
end

-- white ip check
function _M.white_ip_check()
    ngx.log(ngx.DEBUG,"white_ip_check")
    if util.GET("white_ip_check") then
        local IP_WHITE_RULE = _M.get_rule('whiteip')
        local WHITE_IP = util.get_all_ip()
        if IP_WHITE_RULE ~= nil then
            for _, rule in pairs(IP_WHITE_RULE) do
                --if rule ~= "" and rulematch(string.gsub(WHITE_IP,"[.]","_"), string.gsub(rule,"[.]","_"), "jo") then
                if rule ~= "" and rulematch(WHITE_IP, rule, "jo") then     
                    util.log_record('bwlist',WHITE_IP,'WHITE_IP', ngx.var.request_uri, "-", "-")
                    return true
                end
            end
        end
    end
end

-- allow white url
function _M.white_url_check()
    ngx.log(ngx.DEBUG,"white_url_check")
    if util.GET("white_url_check") then
        local URL_WHITE_RULES = _M.get_rule('whiteurl')
        if URL_WHITE_RULES ~= nil then
            local REQ_URI = ngx.var.request_uri
            if URL_WHITE_RULES ~= nil then
                for _,rule in pairs(URL_WHITE_RULES) do
                    if rule ~= "" and rulematch(REQ_URI, rule, "joi") then
                        util.log_record('bwlist',REQ_URI,'WHITE_URL', ngx.var.request_uri, "-", "-")
                        return true
                    end
                end
            end
        end
    end
end

-- white ua check
function _M.white_ua_check()
    ngx.log(ngx.DEBUG,"white_ua_check")
    if util.GET("white_ua_check") then
        local UA_WHITE_RULE = _M.get_rule('whiteua')
        if UA_WHITE_RULE ~= nil then
            local WHITE_UA = util.get_user_agent()
            if WHITE_UA ~= "" then
                for _, rule in pairs(UA_WHITE_RULE) do
                    --if rule ~= "" and rulematch(string.gsub(WHITE_UA,"[.]","_"), string.gsub(rule,"[.]","_"), "joi") then
                    if rule ~= "" and rulematch(WHITE_UA, rule, "joi") then
                        util.log_record('bwlist',WHITE_UA,'WHITE_UA', ngx.var.request_uri, "-", "-")
                        return true
                    end
                end
            end
        end
    end
end

-- white referer check
function _M.white_rf_check()
    ngx.log(ngx.DEBUG,"white_rf_check")
    if util.GET("white_rf_check") then
        local RF_WHITE_RULE = _M.get_rule('whiterf')
        if RF_WHITE_RULE ~= nil then
            local WHITE_RF = util.get_referer()
            if WHITE_RF ~= "" then
                for _, rule in pairs(RF_WHITE_RULE) do
                    if rule ~= "" and rulematch(WHITE_RF, rule, "joi") then
                        util.log_record('bwlist',WHITE_RF,'WHITE_RF', ngx.var.request_uri, "-", "-")
                        return true
                    end
                end
            end
        end
    end
end

--white list check
function _M.white_list_check()
    ngx.log(ngx.DEBUG,"white_list_check")
    if util.GET("white_list_enable") then
        if config.whitelist ~= nil then
            for _,x in ipairs(config.whitelist) do
                local Rule_Token = ""
                if x.ruletype == "args" then
                    Rule_Token = ngx.var.args
                elseif x.ruletype == "url" then
                    Rule_Token = ngx.var.request_uri
                elseif x.ruletype == "useragent" then
                    Rule_Token = util.get_user_agent()
                elseif x.ruletype == "referer" then
                    Rule_Token = util.get_referer()                        
                elseif x.ruletype == "ip" then
                    Rule_Token = util.get_all_ip()
                elseif x.ruletype == "host" then
                    Rule_Token = ngx.var.host
                elseif x.ruletype == "cookie" then
                    Rule_Token = ngx.var.http_cookie
                elseif x.ruletype == "method" then
                    Rule_Token = ngx.var.request_method
                elseif ngx.var.request_method == "POST" and x.ruletype == "request_body" then
                    ngx.req.read_body()
                    Rule_Token = ngx.req.get_body_data()
                end
                if x.enable and rulematch(Rule_Token,x.ruleitem,"joi") then
                    ngx.log(ngx.DEBUG,string.format("white_list_check:%s,%s", x.ruletype,Rule_Token))
                    return true
                end
            end
        end
    end
end

-- deny blackip in dict 
function _M.blackip_v2_check(wafenable)
    local riskip_check = util.GET("risk_ip_check") 
    local blackip_check = util.GET("black_ip_check")
    local wafmodel = util.GET("waf_model")
    if riskip_check or blackip_check or wafmodel == "deny" then
        --if util.GET("black_ip_check") then
        ngx.log(ngx.DEBUG,"blackip_v2_check")
        local BLACK_IP = util.get_all_ip()
        if BLACK_IP ~= "" then
            local blackip = ngx.shared.blackip
            local biparry = util.split(BLACK_IP,",")
            for _, bip in pairs(biparry) do
                --ngx.log(ngx.DEBUG,bip)
                -- blackip
                if blackip_check then
                    --ip
                    local ret,_ = blackip:get(bip)
                    if ret ~= nil and ret > 0 then
                        local attackType = "BLACK_IP" 
                        util.log_record('bwlist',bip, attackType, ngx.var.request_uri, "-", "-")
                        if wafenable == "record" then
                            return true
                        else
                            util.waf_output()
                        end                   
                    end
                    --cip
                    local cip = util.getcip_ip(bip)
                    if cip ~= "" then
                        local retc,_ = blackip:get(cip)
                        if retc ~= nil and retc == 1 then
                            util.log_record('bwlist',cip,'BLACK_IP', ngx.var.request_uri, "-", "-")
                            if wafenable == "record" then
                                    return true
                            else
                                    util.waf_output()
                            end
                        end
                    end
                end

                --riskip
                if riskip_check then
                    local ret,_ = blackip:get('risk'..bip)
                    if ret ~= nil then
                        local attackType = "RISK_IP_LIMIT"
                        if ret == "waf" then
                            attackType = "RISK_IP_WAF"
                        end
                        if wafenable == "record" then
                            util.log_record('bwlist',bip, attackType, ngx.var.request_uri, "-", "-")
                            return true
                        else
                            local riskmodel = util.GET("waf_riskip_model") 
                            --prevent record log repeat
                            if riskmodel == "deny" then
                                util.log_record('bwlist',bip, attackType, ngx.var.request_uri, "-", "-")
                            end
                            util.waf_risk_output(ret)
                        end
                    end
                end

                --badip
                if wafmodel == "deny" then
                    local ret,_ = blackip:get('bad'..bip)
                        if ret ~= nil and ret > 0 then
                        local attackType = "BAD_IP"
                        util.log_record('bwlist',bip, attackType, ngx.var.request_uri, "-", "-")
                        if wafenable == "record" then
                            return true
                        else
                            util.waf_output()
                        end
                    end
                end  

            end
        end
    end
end

-- deny region
function _M.region_deny_check(wafenable)
    local region_deny_type = util.GET("region_deny_type") 
    if region_deny_type == "disable" then
        return
    end
    ngx.log(ngx.DEBUG,"region_deny_check")
    local REGION_CODE = ngx.var.geoip_country_code
    local region = util.GET("region") 
    if region_deny_type == "allow" then
        if REGION_CODE ~= nil and region ~= "" then 
            if not rulematch(REGION_CODE, region, "joi") then
                util.log_record('waf',REGION_CODE,'DENY_REGION', ngx.var.request_uri, "-", rule)
                if wafenable == "on" then
                    util.waf_output()
                end
            end
        end       
    end
    if region_deny_type == "deny" then
        if REGION_CODE ~= nil and region ~= "" then 
            if rulematch(REGION_CODE, region, "joi") then
                util.log_record('waf',REGION_CODE,'DENY_REGION', ngx.var.request_uri, "-", rule)
                if wafenable == "on" then
                    util.waf_output()
                end
            end
        end       
    end
end

-- url deny check
function _M.url_deny_check(wafenable)
    ngx.log(ngx.DEBUG,"waf_urldeny_check")
    if util.GET("waf_urldeny_check") then
        local xhost = util.get_host()
        local REQ_URI = ngx.var.request_uri
        if config.urldeny_rule ~= nil then
            for _,x in ipairs(config.urldeny_rule) do
                --规则启用,对象不为空才往下执行
                if x.deny_enable and x.deny_key ~= "" then
                    if x.deny_host ~= "" then
                        --域名不为空并匹配时只有指定的URL匹配才行
                        if xhost == x.deny_host then
                            local LogT = ""
                            local Rule_Token = ""
                            if x.deny_url ~= "/" then
                                if rulematch(REQ_URI,x.deny_url,"joi") then
                                    if x.deny_type == "referer" then
                                        Rule_Token = util.get_referer()
                                        LogT = "_RF"
                                    elseif x.deny_type == "useragent" then
                                        Rule_Token = util.get_user_agent()
                                        LogT = "_UA"
                                    elseif x.deny_type == "deny_ip" then
                                        Rule_Token = util.get_all_ip()
                                        LogT = "_IP"
                                    elseif x.deny_type == "allow_ip" then    
                                        Rule_Token = util.get_realip() 
                                        LogT = "_IP"
                                        if rulematch(Rule_Token,x.deny_key,"joi") then
                                            return true
                                        else
                                            util.log_record('bwlist',Rule_Token,'URL_DENY'..LogT, REQ_URI, "-", "-")
                                            if wafenable == "record" then                      
                                                return true
                                            else
                                                util.waf_output()
                                            end
                                        end
                                    end
                                    if rulematch(Rule_Token,x.deny_key,"joi") then
                                        util.log_record('bwlist',x.deny_key,'URL_DENY'..LogT, REQ_URI, "-", "-") 
                                        if wafenable == "record" then                      
                                            return true
                                        else
                                            util.waf_output()
                                        end
                                    end
                                end
                            else
                                --url为空时则该域名下的请求进行匹配
                                if x.deny_type == "referer" then
                                    Rule_Token = util.get_referer()
                                    LogT = "_RF"
                                elseif x.deny_type == "useragent" then
                                    Rule_Token = util.get_user_agent()
                                    LogT = "_UA"
                                elseif x.deny_type == "deny_ip" then
                                    Rule_Token = util.get_all_ip()
                                    LogT = "_IP"
                                elseif x.deny_type == "allow_ip" then    
                                    Rule_Token = util.get_realip() 
                                    LogT = "_IP"
                                    if rulematch(Rule_Token,x.deny_key,"joi") then
                                        return true
                                    else
                                        util.log_record('bwlist',Rule_Token,'URL_DENY'..LogT, REQ_URI, "-", "-")
                                        if wafenable == "record" then                      
                                            return true
                                        else
                                            util.waf_output()
                                        end
                                    end                                    
                                end
                                if rulematch(Rule_Token,x.deny_key,"joi") then
                                    util.log_record('bwlist',x.deny_key,'URL_DENY'..LogT, REQ_URI, "-", "-") 
                                    if wafenable == "record" then                      
                                        return true
                                    else
                                        util.waf_output()
                                    end
                                end                    
                            end
                        end
                    else
                        --当域名为空时,url也要不能为空,否则是全局配置，全局配置在ua和ip白名单设置
                        if x.deny_url ~= "/" and rulematch(REQ_URI,x.deny_url,"joi") then
                            local LogT = ""
                            local Rule_Token = ""
                            if x.deny_type == "referer" then
                                Rule_Token = util.get_referer()
                                LogT = "_RF"
                            elseif x.deny_type == "useragent" then
                                Rule_Token = util.get_user_agent()
                                LogT = "_UA"
                            elseif x.deny_type == "deny_ip" then
                                Rule_Token = util.get_all_ip()
                                LogT = "_IP"
                            elseif x.deny_type == "allow_ip" then    
                                Rule_Token = util.get_realip() 
                                LogT = "_IP"
                                if rulematch(Rule_Token,x.deny_key,"joi") then
                                    return true
                                else
                                    util.log_record('bwlist',Rule_Token,'URL_DENY'..LogT, REQ_URI, "-", "-")
                                    if wafenable == "record" then                      
                                        return true
                                    else
                                        util.waf_output()
                                    end
                                end                                
                            end
                            if rulematch(Rule_Token,x.deny_key,"joi") then
                                util.log_record('bwlist',x.deny_key,'URL_DENY'..LogT, REQ_URI, "-", "-") 
                                if wafenable == "record" then                      
                                    return true
                                else
                                    util.waf_output()
                                end
                            end
                        end
                    end
                end
            end
        end
    end
end

-- deny cookie
function _M.cookie_attack_check(wafenable)
    ngx.log(ngx.DEBUG,"cookie_attack_check")
    if util.GET("cookie_check") then
        local COOKIE_RULES = _M.get_rule('cookie')
        if COOKIE_RULES ~= nil then
            local USER_COOKIE = ngx.var.http_cookie
            if USER_COOKIE ~= nil then
                for _, rule in pairs(COOKIE_RULES) do
                    if rule ~="" and rulematch(USER_COOKIE, rule, "jo") then
                        util.log_record('waf',USER_COOKIE,'DENY_COOKIE', ngx.var.request_uri, "-", rule)
                        if wafenable == "on" then
                            util.waf_output()
                        end
                        return true
                    end
                end
            end
        end
    end
    return false
end

-- check head length
function _M.request_head_check(wafenable)
    ngx.log(ngx.DEBUG,"request_head_check")
    local urllength = util.GET("waf_url_length")
    -- local request_length = ngx.var.request_length
    --if tonumber(request_length) > 1048576 then -- 1MB
    --    ngx.exit(ngx.HTTP_REQUEST_ENTITY_TOO_LARGE)
    --end
    if tonumber(urllength) > 0 then
        if #(ngx.var.request_uri) > tonumber(urllength) then
            util.log_record('waf',post_data,'URL_TooLong', ngx.var.request_uri, "-", rule)
            if wafenable == "on" then
                util.waf_output()
            end
            return true
        end
    end
    return false
end

-- deny url
function _M.url_attack_check(wafenable)
    ngx.log(ngx.DEBUG,"url_attack_check")
    if util.GET("url_check") then
        local URL_RULES = _M.get_rule('url')
        if URL_RULES ~= nil then
            local REQ_URI = ngx.var.request_uri
            if URL_RULES ~= nil then
                for _,rule in pairs(URL_RULES) do
                    if rule ~="" and rulematch(REQ_URI,rule,"joi") then
                        util.log_record('waf',REQ_URI,'DENY_URI', ngx.var.request_uri, "-", rule)
                        if wafenable == "on" then
                            util.waf_output()
                        end
                        return true
                    end
                end
            end
        end
    end
    return false
end

-- deny urlargs
function _M.url_args_attack_check(wafenable)
    ngx.log(ngx.DEBUG,"urlargs_attack_check")
    if util.GET("url_args_check") then
        local ARGS_RULES = _M.get_rule('args')
        if ARGS_RULES ~= nil then
            for _,rule in pairs(ARGS_RULES) do
                local REQ_ARGS = ngx.req.get_uri_args()
                for key, val in pairs(REQ_ARGS) do
                    local ARGS_DATA = {}
                    if type(val) == 'table' then
                        if #val > 1 then
                            ngx.log(ngx.DEBUG,string.format("exception key,key:%s",key))
                            return true
                        end
                        if type(val[1]) == "boolean" then
                            return true
                        end
                        ARGS_DATA = table.concat(val, " ")
                    else
                        ARGS_DATA = val
                    end
                    --ngx.log(ngx.DEBUG, ARGS_DATA,rule)
                    if ARGS_DATA and type(ARGS_DATA) ~= "boolean" and rule ~="" and rulematch(unescape(ARGS_DATA), rule, "joi") then
                        util.log_record('waf',ARGS_DATA,'DENY_ARGS', ngx.var.request_uri, "-", rule)
                        if wafenable == "on" then
                            util.waf_output()
                        end
                        return true
                    end
                end
            end
        end
    end
    return false
end

-- deny useragent
function _M.user_agent_attack_check(wafenable)
    ngx.log(ngx.DEBUG,"useragent_attack_check")
    if util.GET("user_agent_check") then
        local USER_AGENT_RULES = _M.get_rule('useragent')
        if USER_AGENT_RULES ~= nil then
            local USER_AGENT = util.get_user_agent()
            if USER_AGENT ~= "" and USER_AGENT_RULES ~= nil then
                for _, rule in pairs(USER_AGENT_RULES) do
                    if rule ~= "" and rulematch(USER_AGENT, rule, "joi") then
                        util.log_record('waf',USER_AGENT,'DENY_UA', ngx.var.request_uri, "-", rule)
                        if wafenable == "on" then
                            util.waf_output()
                        end
                        return true
                    end
                end
            end
        end
    end
    return false
end

-- deny referer
function _M.referer_attack_check(wafenable)
    ngx.log(ngx.DEBUG,"referer_attack_check")
    if util.GET("referer_check") then
        local REFERER_RULE = _M.get_rule('referer')
        if REFERER_RULE ~= nil then
            local referer = util.get_referer()
            if referer ~= "" then
                for _, rule in pairs(REFERER_RULE) do
                    if rule ~= "" and rulematch(referer, rule, "joi") then
                        util.log_record('waf',referer,'DENY_REFERER', ngx.var.request_uri, "-", rule)
                        if wafenable == "on" then
                            util.waf_output()
                        end
                        return true
                    end
                end
            end
        end
    end
end
-- deny postdata
function _M.post_attack_check(wafenable)
    ngx.log(ngx.DEBUG,"post_attack_check")
    if util.GET("post_check") then
        ngx.req.read_body()
        local POST_RULES = _M.get_rule('post')
        if POST_RULES ~= nil then
            for _, rule in pairs(POST_RULES) do
                local POST_ARGS = ngx.req.get_post_args() or {}
                for _, v in pairs(POST_ARGS) do
                    local post_data = ""
                    if type(v) == "table" then
                        if type(v[1]) == "boolean" then
                            return true
                        end                        
                        post_data = table.concat(v, ", ")
                    else
                        post_data = v
                    end
                    --ngx.log(ngx.DEBUG,tostring(post_data))
                    if rule ~= "" and rulematch(tostring(post_data), rule, "joi") then
                        util.log_record('waf',post_data,'DENY_POSTDATA', ngx.var.request_uri, "-", rule)
                        if wafenable == "on" then
                            util.waf_output()
                        end
                        return true
                    end
                end
            end
        end
    end
    return false
end

function _M.post_attack_checkv2(wafenable)
    ngx.log(ngx.DEBUG,"post_attack_check")
    if util.GET("post_check") and ngx.var.request_method == "POST" then
        ngx.req.read_body()
        local POST_RULES = _M.get_rule('post')
        if POST_RULES ~= nil then
            local body_data = ngx.req.get_body_data()
            if body_data then
                local bodys = body_data:gsub("[\r\n]", ",")
                for _, rule in pairs(POST_RULES) do
                    if rule ~= "" and rulematch(bodys, rule, "joi") then
                        util.log_record('waf',bodys,'DENY_POSTDATA', ngx.var.request_uri, "-", rule)
                        if wafenable == "on" then
                            util.waf_output()
                        end
                        return true
                    end
                end
            else
                local body_file = ngx.req.get_body_file()
                if body_file then
                    ngx.log(ngx.DEBUG, string.format("get_body_file,exclude check:%s",body_file))
                    return true
                end
            end
        end
    end
    return false
end


--limit core func
function limit_core_func(limit_host,limit_type,limit_url,limit_key,limit_speed,rateV,limit_downrate)
    local dToken = ""
    if limit_host ~= "" then
        dToken = "host"..limit_host
    end	
    local urlToken = ""
    local logTypePre = ""
    if limit_url ~= "" then
        urlToken = limit_url
        logTypePre = "URL_"
    end	

    if limit_type == "ip" then     
        local logType = logTypePre.."IP_LIMIT"
        ngx.log(ngx.DEBUG,logType)      
        local IP_TOKEN = util.get_realip()
        local AllIp = util.get_all_ip()
        local limit_token = dToken..urlToken..IP_TOKEN
        local iskeepon = true
        if limit_key ~= "" then
            if rulematch(AllIp, limit_key, "jo") then
                limit_token = dToken..urlToken..limit_type..limit_key
                --limit_key不为空并匹配时独立限流,不循环规则
                iskeepon = false
            else
                return iskeepon
            end
        end
        local result = util.Reqlimit(limit_token,logType,rateV,limit_speed,ngx.var.request_uri)
        if iskeepon == false then
            return false
        end
        return result
    end

    if limit_type == "cip" then
        local logType = logTypePre.."CIP_LIMIT"
        ngx.log(ngx.DEBUG,logType) 
        local IP_TOKEN = util.getcip_realip()
        local AllIp = util.get_all_ip()
        local limit_token = dToken..urlToken..IP_TOKEN
        local iskeepon = true
        if limit_key ~= "" then
            if rulematch(AllIp, limit_key, "jo") then
                limit_token = dToken..urlToken..limit_type..limit_key
                --limit_key不为空并匹配时独立限流,不循环规则
                iskeepon = false
            else
                return iskeepon
            end          
        end
        local result = util.Reqlimit(limit_token,logType,rateV,limit_speed,ngx.var.request_uri)	
        if iskeepon == false then
            return false
        end        
        return result
    end

    if limit_type == "ipuri" then	
        local logType = logTypePre.."IPURI_LIMIT"
        ngx.log(ngx.DEBUG,logType)	
        local IP_TOKEN = util.get_realip()	
        if 	IP_TOKEN ~= "" then
            local limit_token = dToken..urlToken..IP_TOKEN..ngx.var.uri
            if limit_key ~= "" and rulematch(limit_token, limit_key, "joi") then
                limit_token = dToken..urlToken..limit_type..limit_key
            end
            local result = util.Reqlimit(limit_token,logType,rateV,limit_speed,ngx.var.request_uri)	
            return result          
        end		
    end    

    if limit_type == "useragent" then		
        local logType = logTypePre.."UA_LIMIT"
        ngx.log(ngx.DEBUG,logType) 
        local UA_TOKEN = util.get_user_agent()
        if UA_TOKEN ~= "" then
            local limit_token = dToken..urlToken..UA_TOKEN
            local iskeepon = true
            if limit_key ~= "" then
                if rulematch(UA_TOKEN, limit_key, "joi") then
                    limit_token = dToken..urlToken..limit_type..limit_key
                    --limit_key不为空并匹配时独立限流,不循环规则
                    iskeepon = false
                else
                    return iskeepon
                end               
            end
            local result = util.Reqlimit(limit_token,logType,rateV,limit_speed,ngx.var.request_uri)
            if iskeepon == false then
                return false
            end
            return result
        end
    end					                   

    if limit_type == "referer" then	
        local logType = logTypePre.."RF_LIMIT"
        ngx.log(ngx.DEBUG,logType)
        local RF_TOKEN = util.get_referer()
        if RF_TOKEN ~= "" then
            local limit_token = dToken..urlToken..RF_TOKEN
            local iskeepon = true
            if limit_key ~= "" then
                if rulematch(RF_TOKEN, limit_key, "joi") then
                    limit_token = dToken..urlToken..limit_type..limit_key
                    --limit_key不为空并匹配时独立限流,不循环规则
                    iskeepon = false
                else
                    return iskeepon
                end                              
            end
            local result = util.Reqlimit(limit_token,logType,rateV,limit_speed,ngx.var.request_uri)
            if iskeepon == false then
                return false
            end            
            return result
        end
    end	

    if limit_type == "args" then		
        local logType = logTypePre.."ARGS_LIMIT"
        ngx.log(ngx.DEBUG,logType)
        local ARGS_TOKEN = ngx.var.args
        if ARGS_TOKEN ~= "" and limit_key ~= "" then
            limit_token = dToken..urlToken..limit_type..limit_key
            if rulematch(ARGS_TOKEN, limit_key, "joi") then
                local result = util.Reqlimit(limit_token,logType,rateV,limit_speed,ngx.var.request_uri)
                --因为args 的limit_key不为空，所以匹配后独立限流,不循环规则，所以直接返回false
                return false
            end	        
        end
    end	     

    if limit_type == "all" then	
        local logType = logTypePre.."ALL_LIMIT"
        ngx.log(ngx.DEBUG,logType)	
        local limit_token = dToken..urlToken.."all"
        local result = util.Reqlimit(limit_token,logType,rateV,limit_speed,ngx.var.request_uri)	
        return result			
    end
end

--limit downrate func
function limit_rate_func(limit_host,limit_type,limit_url,limit_key,limit_downrate,rate_score,last_downrate)
    local Rule_Token = ""
    if limit_type == "args" then
        Rule_Token = ngx.var.args
    elseif limit_type == "url" then
        Rule_Token = ngx.var.request_uri
    elseif limit_type == "useragent" then
        Rule_Token = util.get_user_agent()
    elseif limit_type == "referer" then
        Rule_Token = util.get_referer()
    elseif limit_type == "ip" then
        Rule_Token = util.get_all_ip()
    end

    local xscore = 0
    --host
    local xhost = util.get_host()
    if limit_host ~= "" then
        if xhost == limit_host then
           xscore = xscore + 8
        else
          return rate_score,last_downrate
        end
    end
    if limit_host == "" then
        xscore = xscore + 4
    end

    --url
    if limit_url ~= "/" then
       if rulematch(ngx.var.request_uri, limit_url, "joi") then
          xscore = xscore + 4
       else
          return rate_score,last_downrate
       end
    end
    if limit_url == "/" then
        xscore = xscore + 2
    end

    --key
    if limit_key ~= "" then
       if rulematch(Rule_Token,limit_key,"joi") then
          xscore = xscore + 2
       else
          if limit_type == "all" then
             xscore = xscore + 1
          else
             return rate_score,last_downrate
          end
       end
    end
    if limit_key == "" then
        xscore = xscore + 1
    end

    if xscore > 0 and xscore > rate_score then
        return xscore, limit_downrate
    end
    return rate_score,last_downrate        
end

--custom_urllimit
function _M.custom_urllimit_check()
    ngx.log(ngx.DEBUG,"reqlimit_rule")
	if config.reqlimit_rule ~= nil then
        --初始化一个速率，当匹配到第一条规则时，重置速率值，往后的匹配到的规则也不再重置该速率
        local now_limit_rate = 0
        local rate_score = 0

        --针对域名进行设置
        local xhost = util.get_host()
		for _,x in ipairs(config.reqlimit_rule) do
			--local rateV = x.limit_rate
            --local rateV = tonumber(string.match(x.limit_rate, '(.*)/'))
			--local expireT = tonumber(string.match(x.limit_rate, '/(.*)'))
            --限制下载速度
            if x.limit_enable == "true" and x.limit_downrate > 0 then	
                rate_score, now_limit_rate = limit_rate_func(x.limit_host,x.limit_type,x.limit_url,x.limit_key,x.limit_downrate,rate_score,now_limit_rate)
            end

			if x.limit_enable == "true" and x.limit_rate > 0 then	
                --判断是否继续循环规则	
                local isKeepOn = true
                --有设置域名的情况
                if xhost == x.limit_host then
                    --URL非斜杠时限制
				    if x.limit_url ~= "/" then                     
					    if rulematch(ngx.var.request_uri, x.limit_url, "joi") then
                            isKeepOn = limit_core_func(x.limit_host,x.limit_type,x.limit_url,x.limit_key,x.limit_speed,x.limit_rate)
                        end
                    else
                        isKeepOn = limit_core_func(x.limit_host,x.limit_type,"",x.limit_key,x.limit_speed,x.limit_rate)
                    end
                --没设置域名的情况，else是将有域名的和没有域名的计数进行排斥统计，避免重复计数
                else 
                    --当限流规则的域名为空才走全局已经启用的规则。不为空如果和请求的域名不相等时不需要处理。例如：限流规则里全部设置了域名时，避免不同域名的请求走到全局无域名的逻辑。
                    if x.limit_host == "" then
                        if x.limit_url ~= "/" then                     
                            if rulematch(ngx.var.request_uri, x.limit_url, "joi") then
                                isKeepOn =  limit_core_func("",x.limit_type,x.limit_url,x.limit_key,x.limit_speed,x.limit_rate)
                            end
                        else
                            isKeepOn = limit_core_func("",x.limit_type,"",x.limit_key,x.limit_speed,x.limit_rate)
                        end
                    end
                end

                if isKeepOn == false then
                    ngx.log(ngx.DEBUG, string.format("%s:ISKEEPON", isKeepOn))
                    break                   
                end
			end		 			
		end

        --限制下载速度
        if now_limit_rate ~= nil and now_limit_rate > 0 then
            ngx.log(ngx.DEBUG, string.format("limit_downrate:%d", now_limit_rate))
            ngx.var.limit_rate = string.format("%dk", now_limit_rate)
        end

	end
    return false
end


function _M.waf_check(wafenable)
    ngx.log(ngx.DEBUG,"waf_check")
	--if wafenable == "on" or wafenable == "record"  then
		--exclude inranet request
		if util.GET("waf_exclude_intranet") then
			local REMOTE_ADDR = ngx.var.remote_addr
			if rulematch(REMOTE_ADDR,"^192\\.168\\.|^10\\.|^172\\.(16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)\\.","jo") then
                ngx.log(ngx.DEBUG,"waf_exclude_intranet")
				return true
			end 
            if rulematch(REMOTE_ADDR,"^100\\.[6-9][0-9]\\.|^100\\.[1-2][0-9]{2}\\.","jo") then    
                local xrealIp = util.get_realip()
                if rulematch(xrealIp,"^192\\.168\\.|^10\\.|^172\\.(16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)\\.","jo") then
                    ngx.log(ngx.DEBUG,string.format("waf_exclude_intranet,ip:%s",xrealIp))
                    return true
                end 
			end                    
		end

		--exclude js css xml
		if util.GET("waf_exclude_static_file") then
			local REQ_URI = ngx.var.uri
			if rulematch(REQ_URI,"\\.(js|ico|css|jpeg|jpg|png|swf|webp|gif|xml|mp4|mp3|m3u8|ts|mjs|ttf|scss|txt)$","joi") then
                ngx.log(ngx.DEBUG,"waf_exclude_static")
				return true
			end            
		end     

        if _M.request_head_check(wafenable) then       
        elseif _M.user_agent_attack_check(wafenable) then
        elseif _M.url_attack_check(wafenable) then
        elseif _M.cookie_attack_check(wafenable) then
        elseif _M.referer_attack_check(wafenable) then            
        elseif _M.url_args_attack_check(wafenable) then
        elseif _M.post_attack_checkv2(wafenable) then
        else
            return
        end
	--end
end

-- limit check
function _M.req_limit_check()
    ngx.log(ngx.DEBUG,"reqlimit_check")
    local limitenable = util.GET("reqlimit_enable")
    if limitenable == "on" or limitenable == "record" then
 		--exclude inranet request
        if util.GET("reqlimit_exclude_intranet") then
			local REMOTE_ADDR = ngx.var.remote_addr
			if rulematch(REMOTE_ADDR,"^192\\.168\\.|^10\\.|^172\\.(16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)\\.","jo") then
                ngx.log(ngx.DEBUG,string.format("reqlimit_exclude_intranet,ip:%s",REMOTE_ADDR))
				return false
			end        
            -- aliyun slb
			--if rulematch(REMOTE_ADDR,"^100\\.(120|121|123|124|125)\\.","jo") then
            if rulematch(REMOTE_ADDR,"^100\\.[6-9][0-9]\\.|^100\\.[1-2][0-9]{2}\\.","jo") then    
                local xrealIp = util.get_realip()
                if rulematch(xrealIp,"^192\\.168\\.|^10\\.|^172\\.(16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)\\.","jo") then
                    ngx.log(ngx.DEBUG,string.format("reqlimit_exclude_intranet,ip:%s",xrealIp))
                    return false
                end 
			end             
		end
        --排除子请求。
        if util.GET("reqlimit_exclude_subrequest") then
            if ngx.is_subrequest then
                    ngx.log(ngx.DEBUG,"reqlimit_exclude_subrequest")
                    return false
            end
        end        

        --排除内部请求,例如404下一跳等,但对于rewrite break的请求也会忽略。
        if util.GET("reqlimit_exclude_internalrequest") then
            if ngx.req.is_internal() then
                    ngx.log(ngx.DEBUG,"reqlimit_exclude_internalrequest")
                    return false
            end
        end

		--exclude js css xml
		if util.GET("reqlimit_exclude_static") then
			--local REQ_URI = ngx.var.request_uri
            local REQ_URI = ngx.var.uri
			if rulematch(REQ_URI,"\\.(js|ico|css|jpeg|jpg|png|swf|webp|gif|xml|mp4|mp3|m3u8|ts|mjs|ttf|scss|txt)$","joi") then
                ngx.log(ngx.DEBUG,"reqlimit_exclude_static")
				return false
			end
		end 

        _M.custom_urllimit_check()
    else
        return false
    end
end

-- waf start
function _M.check()
    local wafenable = util.GET("waf_enable")
    if wafenable ~= "on" and wafenable ~= "record"  then
        return
    end
    if _M.exclude_action() then  
    elseif _M.white_list_check() then                    
    elseif _M.blackip_v2_check(wafenable) then     
    elseif _M.url_deny_check(wafenable) then             
    elseif _M.region_deny_check(wafenable) then   
    elseif _M.req_limit_check() then
    elseif _M.waf_check(wafenable) then
    else
        return
    end
end

return _M
